package com.xyy.saas.payment.cashier.core.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.adpater.config.BankConfigManager;
import com.xyy.saas.payment.adpater.jd.common.JDConstants;
import com.xyy.saas.payment.adpater.pingxx.constants.BusinessOrderTypeConfig;
import com.xyy.saas.payment.cashier.core.CashierConstants;
import com.xyy.saas.payment.cashier.core.bo.CashierGeneralCodeBO;
import com.xyy.saas.payment.cashier.core.bo.CashierPayBO;
import com.xyy.saas.payment.cashier.core.bo.CashierPayInfoBO;
import com.xyy.saas.payment.cashier.core.bo.CashierPrePayBO;
import com.xyy.saas.payment.cashier.core.vo.*;
import com.xyy.saas.payment.common.Builder;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.core.EncryptService;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.PayBo;
import com.xyy.saas.payment.cores.bo.PrepayBo;
import com.xyy.saas.payment.cores.bo.PrepayJumpCashierBo;
import com.xyy.saas.payment.cores.enums.*;
import com.xyy.saas.payment.cores.vo.PrepayJumpCashierVo;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.dao.model.PayResultOrder;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.dao.model.ThirdUserInfo;
import com.xyy.saas.payment.payment.core.dto.PayDto;
import com.xyy.saas.payment.payment.core.dto.PrepayDto;
import com.xyy.saas.payment.service.PayResultOrderService;
import com.xyy.saas.payment.payment.core.service.PaymentService;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.JSONUtils;
import com.xyy.saas.payment.util.PayTimeExpireUtil;
import com.xyy.saas.payment.util.RedisUtil;
import com.xyy.util.StringUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 收银台逻辑部分
 * @author dake
 * @date 20200327
 */
@Service
@DependsOn("apolloUtil")
public class CashierService implements InitializingBean {

    private static Logger LOGGER = LoggerFactory.getLogger(CashierService.class);
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private PrePayOrderService prePayOrderService;
    @Autowired
    private PayResultOrderService payResultOrderService;
    @Autowired
    private Map<String, EncryptService> encryptServiceMap;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private BankConfigManager bankConfigManager;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;

    private Map<String, BusinessOrderTypeConfig> appIdMap = JSONUtils.parse2Map(ApolloUtil.getProperty(Constants.PINGXX_BUSINESS_ORDER_CONFIG), String.class, BusinessOrderTypeConfig.class);

    public CashierPrePayBO prepay(CashierPrePayVO cashierPrePayVO) throws PaymentException {
        cashierPrePayVO.checked();

        LOGGER.info("CashierService#prepay cashierPrePayVO:{}", JSONUtils.toJSON(cashierPrePayVO));
        PrepayDto prepayDto = cashierPrePayVO.convert2PrepayDto();
        ResultVO<PrepayBo> prepayBoResultVO = paymentService.prepay(prepayDto);

        if (prepayBoResultVO.getResult() == null || StringUtil.isEmpty(prepayBoResultVO.getResult().getPayNo())) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }

        if (appIdMap.get(cashierPrePayVO.getBusinessOrderType()).isCompliance()) {
            if (paymentService.checkCompliance(cashierPrePayVO.getPayerId(), cashierPrePayVO.getPayerName(), cashierPrePayVO.getSellerName())) {
                prepayDto.setPayerId(cashierPrePayVO.getPayerId());
                prepayDto.setPayerName(cashierPrePayVO.getPayerName());
                prepayDto.setSellerName(cashierPrePayVO.getSellerName());
            } else {
                LOGGER.warn("CashierService prepay warn compliance cashierPrePayVO:{}", JSONUtils.toJSON(cashierPrePayVO));
                throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
            }
        }

        String payNo = prepayBoResultVO.getResult().getPayNo();
        String payNoSecret = getPayNoSecret(payNo);
        return CashierPrePayBO.instance(payNo, payNoSecret);
    }

    public CashierPayInfoBO payInfo(CashierPayInfoVO payInfoVO) throws PaymentException {
        PrePayOrder prePayOrder = getAndCheck(payInfoVO.getPayNo());
        LOGGER.info("CashierService#payinfo payInfoVO：{} PrePayOrder: {}", JSONUtils.toJSON(payInfoVO), JSONUtils.toJSON(prePayOrder));
        return genCashierPayInfoBO(payInfoVO.getPayNo(), prePayOrder, payInfoVO.getPayMode());
    }

    /**
     * 收银台支付接口
     *
     * @param payVO
     * @return
     * @throws PaymentException
     */
    public CashierPayBO pay(CashierPayVO payVO, String cashierVersion) throws PaymentException {
        if (payVO.notParamChecked()) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }
        // 使用分布式锁，验证是否重复提交
        String lockKey = Constants.ORDER_PAY_DEGRATE_REDIS_KEY_PREFIX + payVO.getPayNo() + ":" + payVO.getPayMode();
        boolean tryLock = redisUtil.tryLock(lockKey);
        try {
            if (!tryLock) {
                LOGGER.info("CashierService#pay 重复提交 {} ", JSONUtils.toJSON(payVO));
                throw new PaymentException(ResultCodeEnum.ORDER_IN_PROCESS);
            }

            LOGGER.info("CashierService#pay CashierPayVO:{}", JSONUtils.toJSON(payVO));
            PrePayOrder prePayOrder = getAndCheck(payVO.getPayNo());
            LOGGER.info("CashierService#pay , PrePayOrder: {}", JSONUtils.toJSON(prePayOrder));
            if (!ChannelEnum.JDAGGREGATE_WX.getCode().equals(payVO.getChannel())) {
                List<String> payChannels = getPayChannels(payVO.getPayMode(), prePayOrder.getBusinessOrderType());
                if (!payChannels.contains(payVO.getChannel())) {
                    throw new PaymentException(ResultCodeEnum.CASHIER_PAY_CHANNEL_ERROR);
                }
            }

            String redisCachePayLoadKey = Constants.ORDER_PAY_LOAD_REDIS_KEY_PREFIX + payVO.getPayNo() + payVO.getChannel();
            String payload = redisClient.get(redisCachePayLoadKey);

            if (StringUtils.isEmpty(payload)) {
                PayDto payDto = payVO.convert2PayDto();
                payDto.setPayNo(getPayNo(payVO.getPayNo()));
                payDto.setAmount(prePayOrder.getAmount());
                payDto.setBusinessOrderType(prePayOrder.getBusinessOrderType());
                Object channelObj = JSONUtils.getValueFromJson(prePayOrder.getExtra(), "channel");
                String channel = channelObj == null ? "": (String) channelObj;
                if (ChannelEnum.JDAGGREGATE_ALIPAY.getCode().equals(channel)) {
                    payDto.setChannel(ChannelEnum.JDAGGREGATE_ALIPAY.getCode());
                }
                if (StringUtils.isEmpty(cashierVersion) && PayModeEnum.APP.getMode().equals(payDto.getPayMode())) {
                    payDto.setSdkMode(CashierConstants.CASHIER_SDK_V1);
                } else if (StringUtils.isNotEmpty(cashierVersion)) {
                    payDto.setSdkMode(cashierVersion);
                } else {
                    payDto.setSdkMode(CashierConstants.CASHIER_SDK_V2);
                }
                if (ChannelEnum.AGGREGATE_ALI.getCode().equals(payVO.getChannel()) && PayModeEnum.H5.getMode().equals(payVO.getPayMode())) {
                    payDto.setPayResultMode(PayResultModeEnum.BASIC.getMode());
                }
                ResultVO<PayBo> payBoResultVO = paymentService.channelDegradedPay(payDto);
                LOGGER.info("CashierService#pay payBoResult{},result {},msg:{}",prePayOrder.getPayNo(), payBoResultVO.getCode().equals(ResultCodeEnum.SUCCESS.getCode()),payBoResultVO.getMsg());

                if (payBoResultVO != null && payBoResultVO.getCode().equals(ResultCodeEnum.SUCCESS.getCode()) && payBoResultVO.getResult() != null) {
                    payload = payBoResultVO.getResult().getPayLoad();
                    redisClient.setEx(redisCachePayLoadKey, payload, PayTimeExpireUtil.handleTimeExpire(payDto.getChannel(), payDto.getPayMode(), payDto.getTimeExpire()));
                } else if (payBoResultVO != null && !payBoResultVO.getCode().equals(ResultCodeEnum.SUCCESS.getCode())) {
                    throw new PaymentException(payBoResultVO.getCode(), payBoResultVO.getMsg());
                } else {
                    LOGGER.error("CashierService#pay error no results payVO:{}", JSONUtils.toJSON(payVO));
                    throw new PaymentException(ResultCodeEnum.SYSTEM_BUSY_ERROR);
                }
            }

            return CashierPayBO.instance(payVO.getPayNo(), payload, (prePayOrder.getTimeExpire().getTime() - System.currentTimeMillis())/1000 + 60);
        } finally {
            if (tryLock) {
                redisUtil.unLock(lockKey);
            }
        }
    }

    public Boolean payResult(PayResultVO payResultVO) throws PaymentException {
        String payNo = getPayNo(payResultVO.getPayNo());
        PayResultOrder order = payResultOrderService.selectByPayNoAndChannel(payNo, payResultVO.getChannel());
        return order != null;
    }

    public CashierGeneralCodeBO generalCode(CashierGeneralCodeVO cashierGeneralCodeVO) throws PaymentException {
        PrePayOrder prePayOrder = getAndCheck(cashierGeneralCodeVO.getPayNo());
        return genCashierGeneralCodeBO(cashierGeneralCodeVO.getPayNo(), prePayOrder);
    }

    /**
     * 根据加密的订单号获取业务线
     * @return
     */
    public PrePayOrder getPrePayOrderByPayNoToken(String payNoToken) throws PaymentException {
        if(StringUtils.isBlank(payNoToken)) {
            throw new PaymentException(ResultCodeEnum.TRADE_ORDER_IS_NULL);
        }

        try {
            String payNo = getPayNo(payNoToken);
            PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(payNo);

            if(prePayOrder == null) {
                LOGGER.error("CashierService#getBusinessOrderTypeByPayNoToken:{} cannot find PrePayOrder, payNo: {}", payNoToken, payNo);
                throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
            }

            return prePayOrder;
        }
        catch(Exception ex) {
            LOGGER.error(ex.getMessage() + "， payNoToken=" + payNoToken, ex);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    private PrePayOrder getAndCheck(String payNoToken) throws PaymentException {
        String payNo = getPayNo(payNoToken);
        PrePayOrder prePayOrder = prePayOrderService.getPrePayOrderByPayNo(payNo);
        if (prePayOrder == null) {
            LOGGER.error("CashierService#getAndCheck:{} cannot find PrePayOrder, payNo: {}", payNoToken, payNo);
            throw new PaymentException(ResultCodeEnum.TRADE_NOT_EXIT_ERROR);
        }
        // 验证是否已经交易
        List<PayResultOrder> payResultOrders = payResultOrderService.selectListByPayNo(payNo);
        if(payResultOrders != null && payResultOrders.size() > 0) {
            throw new PaymentException(ResultCodeEnum.ORDER_ALREADY_PAY);
        }
        if ((prePayOrder.getTimeExpire().getTime() - Constants.CASHIER_TIME_FIX) <= System.currentTimeMillis()) {
            LOGGER.error("CashierService#getAndCheck, prePayOrder expired payNo: {}", payNo);
            throw new PaymentException(ResultCodeEnum.ORDER_TIME_EXPIRE);
        }
        return prePayOrder;
    }

    private CashierPayInfoBO genCashierPayInfoBO(String payNo, PrePayOrder prePayOrder, String payMode) throws PaymentException {
        Integer amount = prePayOrder.getAmount();
        Map<String, String> map = Optional.ofNullable(JSONUtils.parse2Map(prePayOrder.getExtra(), String.class, String.class)).orElse(Maps.newHashMap());
        String subject = map.get("subject");
        subject = Optional.ofNullable(subject).orElse("");
        long time = (prePayOrder.getTimeExpire().getTime() - System.currentTimeMillis()) / 1000;
        // ping++支付 在调用ping++ order.pay接口时, 如果不传超时时间(我们目前使用的方式)会以order.create时设置的超时时间为准
        // 在此种情况下, 超时时间-当前时间<60秒会无法调起order.pay接口, 所以给前端的时间提前扣减60
        time = time > 60 ? time - 60 : 0;
        long payOrderTime = prePayOrder.getCreateTime().getTime();
        List<CashierPayInfoBO.ChannelType> channelTypes = getPayChannels(payMode, prePayOrder.getBusinessOrderType()).stream().map(this::initChannelType).collect(Collectors.toList());
        String targetUrl = map.get("cashierJumpUrl");
        return Builder.of(CashierPayInfoBO::new)
                .with(CashierPayInfoBO::setAmount, amount)
                .with(CashierPayInfoBO::setExpireTime, time)
                .with(CashierPayInfoBO::setPayNo, payNo)
                .with(CashierPayInfoBO::setSubject, subject)
                .with(CashierPayInfoBO::setChannels, channelTypes)
                .with(CashierPayInfoBO::setPayOrderTime, payOrderTime)
                .with(CashierPayInfoBO::setTargetUrl, targetUrl)
                .build();
    }

    private CashierGeneralCodeBO genCashierGeneralCodeBO(String payNo, PrePayOrder prePayOrder) throws PaymentException {
        Integer amount = prePayOrder.getAmount();
        Map<String, String> map = Optional.ofNullable(JSONUtils.parse2Map(prePayOrder.getExtra(), String.class, String.class)).orElse(Maps.newHashMap());
        String subject = map.get("subject");
        subject = Optional.ofNullable(subject).orElse("");
        long time = (prePayOrder.getTimeExpire().getTime() - System.currentTimeMillis()) / 1000;
        // ping++支付 在调用ping++ order.pay接口时, 如果不传超时时间(我们目前使用的方式)会以order.create时设置的超时时间为准
        // 在此种情况下, 超时时间-当前时间<60秒会无法调起order.pay接口, 所以给前端的时间提前扣减60
        time = time > 60 ? time - 60 : 0;
        long payOrderTime = prePayOrder.getCreateTime().getTime();
        String url = ApolloUtil.getProperty(Constants.H5_CASHIER_ADDRESS_CONFIG);
        String qrCode = getGeneralCode(url, amount, time, payNo, subject, payOrderTime);
        return Builder.of(CashierGeneralCodeBO::new)
                .with(CashierGeneralCodeBO::setAmount, amount)
                .with(CashierGeneralCodeBO::setExpireTime, time)
                .with(CashierGeneralCodeBO::setPayNo, payNo)
                .with(CashierGeneralCodeBO::setSubject, subject)
                .with(CashierGeneralCodeBO::setQrCode, qrCode)
                .with(CashierGeneralCodeBO::setPayOrderTime, payOrderTime)
                .build();
    }

    private String getGeneralCode(String url, int amount, long time, String payNo, String subject, long payOrderTime) throws PaymentException {
        String payNoToken;
        try {
            payNoToken =  URLEncoder.encode(getPayNoSecret(payNo), "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        return url + "?amount=" + amount + "&time=" + time + "&payNo=" + payNoToken+ "&subject=" + subject + "&payOrderTime=" + payOrderTime;
    }


    private List<String> getPayChannels(String payMode, String businessOrderType) throws PaymentException {
        try {
            List<String> channels = JSONUtils.parse2List(ApolloUtil.getProperty(String.format(Constants.CASHIER_CHANNEL, payMode, businessOrderType)), String.class);
            if (CollectionUtils.isEmpty(channels)) {
                throw new PaymentException(ResultCodeEnum.CASHIER_PAY_CHANNEL_ERROR);
            }
            return channels;
        } catch (Exception e) {
            throw new PaymentException(ResultCodeEnum.CASHIER_PAY_CHANNEL_ERROR);
        }
    }

    private CashierPayInfoBO.ChannelType initChannelType(String code) {
        ChannelEnum anEnum = ChannelEnum.getChannelEnum(code);
        assert anEnum != null;
        return new CashierPayInfoBO.ChannelType(anEnum.getCode(), anEnum.getName());
    }

    /**
     * 对加密的payNo解密
     * @param payNoToken 密文
     * @return payNo
     */
    public String getPayNo(String payNoToken) throws PaymentException {
        if (payNoToken == null || payNoToken.length() <= 2) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }
        String version = payNoToken.substring(0, 2);
        String token = payNoToken.substring(2);
        EncryptService encryptService = encryptServiceMap.get(version);
        String secretKey = ApolloUtil.getProperty(Constants.AES_ENCRYPT_KEY);
        try {
            return encryptService.decrypt(token, secretKey);
        } catch (Exception e) {
            LOGGER.error("解密失败, content: {}, e: ", token, e);
            throw new PaymentException(ResultCodeEnum.DECRYPT_ERROR);
        }
    }

    /**
     * 对payNo明文加密
     * @param payNo 明文
     * @return 加密之后
     */
    private String defaultVersion = "01";
    private String getPayNoSecret(String payNo) throws PaymentException {
        EncryptService encryptService = encryptServiceMap.get(defaultVersion);
        String secretKey = ApolloUtil.getProperty(Constants.AES_ENCRYPT_KEY);
        try {
            String encrypt = encryptService.encrypt(payNo, secretKey);
            String payNoToken = defaultVersion + encrypt;
            LOGGER.info("CashierService#getPayNoSecret, payNo: {}, payNoToken: {}", payNo, payNoToken);
            return payNoToken;
        } catch (Exception e) {
            LOGGER.error("加密失败, content: {}, e: ", payNo, e);
            throw new PaymentException(ResultCodeEnum.ENCRYPT_ERROR);
        }
    }


    public PrepayJumpCashierBo getCashierPath(PrepayJumpCashierVo prepayJumpCashierVo, String payNo) throws PaymentException {
        PrepayJumpCashierBo prepayJumpCashierBo = new PrepayJumpCashierBo();

        // 若为京东聚合支付渠道
        if (ChannelEnum.JDAGGREGATE_WX.equals(prepayJumpCashierVo.getChannel())) {
            if(prepayJumpCashierVo.getCashierEnum().equals(CashierEnum.TO_MINI)) {
                // 获取小程序信息 ， 拼接参数
                String channel = prepayJumpCashierVo.getChannel().getCode().split("_")[1];
                String url = ApolloUtil.getProperty(Constants.NAVIGATE_PATH_JDAGGREGATE + "_" + CashierEnum.TO_MINI.getMode() + "_" + channel);
                String path = getMiniNavigatePath(url, prepayJumpCashierVo, payNo, channel);
                // PC 微信支付，微信渠道需要返回完整URL
                path = postProcessPath(path, prepayJumpCashierVo);
                prepayJumpCashierBo.setPayLoad(path);
                prepayJumpCashierBo.setPayNO(payNo);
                return prepayJumpCashierBo;
            }
            if(prepayJumpCashierVo.getCashierEnum().equals(CashierEnum.TO_H5)) {
                String url = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + CashierEnum.TO_H5.getMode());
                String path = getH5NavigatePath(url, prepayJumpCashierVo, payNo);
                prepayJumpCashierBo.setPayLoad(path);
                prepayJumpCashierBo.setPayNO(payNo);
                return prepayJumpCashierBo;
            }
        } else {
            if(prepayJumpCashierVo.getCashierEnum().equals(CashierEnum.TO_MINI)) {
                // 获取小程序信息 ， 拼接参数
                String channel = prepayJumpCashierVo.getChannel().getCode().split("_")[1];
                String url = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + CashierEnum.TO_MINI.getMode() + "_" + channel);
                String path =  getMiniNavigatePath(url, prepayJumpCashierVo, payNo ,channel);
                //PC 微信支付宝走富民或平安，微信渠道需要返回完整URL
                path = postProcessPath(path, prepayJumpCashierVo);
                prepayJumpCashierBo.setPayLoad(path);
                prepayJumpCashierBo.setPayNO(payNo);
                return prepayJumpCashierBo;
            }
            if(prepayJumpCashierVo.getCashierEnum().equals(CashierEnum.TO_H5)) {
                String url = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + CashierEnum.TO_H5.getMode());
                String path = getH5NavigatePath(url, prepayJumpCashierVo, payNo);
                prepayJumpCashierBo.setPayLoad(path);
                prepayJumpCashierBo.setPayNO(payNo);
                return prepayJumpCashierBo;
            }
        }

        throw new PaymentException(ResultCodeEnum.UN_SUPPORT_METHOD);
    }

    private String postProcessPath(String path, PrepayJumpCashierVo prepayJumpCashierVo){
        if (prepayJumpCashierVo.getChannel() == ChannelEnum.JDAGGREGATE_WX && prepayJumpCashierVo.getTerminalTypeEnum() == TerminalTypeEnum.PC) {
            String urlPrefix = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + "WX_URL_jd").replace("{businessType}", prepayJumpCashierVo.getBusinessType());
            JSONObject jsonObject = JSON.parseObject(path);
            String wxMinPath = jsonObject.getString("WXMiniPath");
            String p = wxMinPath.substring(wxMinPath.indexOf("?") + 1);
            return urlPrefix + "?" + p;
        }
        if (prepayJumpCashierVo.getChannel() == ChannelEnum.AGGREGATE_WX && prepayJumpCashierVo.getTerminalTypeEnum() == TerminalTypeEnum.PC) {
            ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(prepayJumpCashierVo.getAccountId());
            String urlPrefix = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + "WX_URL_china_bank");

            if(thirdUserInfo != null && Constants.CHANNEL_PINGAN_NAME.equals(thirdUserInfo.getThirdType())) {
                urlPrefix = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + "WX_URL_pingan");
            }
            // POP大商户切平安
            else if(Boolean.parseBoolean(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_TO_PINGAN_SWITCH, Boolean.FALSE.toString())) &&
                    Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), prepayJumpCashierVo.getAccountId()))
            {
                urlPrefix = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + "WX_URL_pingan");
            }
            else if(bankConfigManager.isFuminBankAggregatePay(prepayJumpCashierVo.getBusinessOrderType())) {
                urlPrefix = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + "WX_URL");
            }

            JSONObject jsonObject = JSON.parseObject(path);
            String wxMinPath = jsonObject.getString("WXMiniPath");
            String p = wxMinPath.substring(wxMinPath.indexOf("?") + 1);
            return urlPrefix + "?" + p;
        }

        return path;
    }

    private String getMiniNavigatePath(String path, PrepayJumpCashierVo prepayJumpCashierVo, String payNo, String channel) throws PaymentException {
        String payNoToken;
        try {
            payNoToken =  URLEncoder.encode(getPayNoSecret(payNo), "utf-8");
        } catch(UnsupportedEncodingException e) {
            LOGGER.error("CashierService#getMiniNavigatePath error payNo:{}",payNo,e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        // 京东聚合支付渠道_微信
        if (ChannelEnum.JDAGGREGATE_WX.equals(prepayJumpCashierVo.getChannel())) {
            JSONObject jsonPath = JSONObject.parseObject(path);
            jsonPath.put("WXMiniUserName", JDConstants.aggregateWxMiniUserName("", prepayJumpCashierVo.getBusinessType()));
            //jsonPath.put("Appid", JDConstants.aggregateWxAppID("", prepayJumpCashierVo.getBusinessType()));
            path = jsonPath.toJSONString();
            return String.format(path, prepayJumpCashierVo.getAmount(), payNoToken, prepayJumpCashierVo.getBusinessOrderNo(), prepayJumpCashierVo.getSubject(), ChannelEnum.JDAGGREGATE_WX.getCode());
        }
        // 其余渠道
        else {
            ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(prepayJumpCashierVo.getAccountId());
            // 如果是支付宝需要再次encode
            if(Constants.CHANNEL_ALI_NAME.equals(channel)) {
                try {
                    payNoToken = URLEncoder.encode(payNoToken, "utf-8");
                } catch(UnsupportedEncodingException e) {
                    LOGGER.error("CashierService#getMiniNavigatePath error payNo:{}",payNo,e);
                    throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
                }
                if (ChannelEnum.JDAGGREGATE_ALIPAY.equals(prepayJumpCashierVo.getChannel())) {
                    path = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + CashierEnum.TO_MINI.getMode() + "_" + channel + "_" + Constants.PINGAN);
                }
                // 平安小程序
                else if(thirdUserInfo != null && Constants.PINGAN.equals(thirdUserInfo.getThirdType())) {
                    path = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + CashierEnum.TO_MINI.getMode() + "_" + channel + "_" + Constants.PINGAN);
                }
                // POP大商户切平安
                else if(Boolean.parseBoolean(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_TO_PINGAN_SWITCH, Boolean.FALSE.toString())) && Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), prepayJumpCashierVo.getAccountId())) {
                    path = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + CashierEnum.TO_MINI.getMode() + "_" + channel + "_" + Constants.PINGAN);
                }
                // 如果是他人代付直连支付宝，则使用平安渠道
                else if (Boolean.parseBoolean(ApolloUtil.getProperty("transOtherPayDirectAlipayToPingan", "false")) && StringUtils.isNotBlank(prepayJumpCashierVo.getMapJson()) && prepayJumpCashierVo.getMapJson().contains("other_pay")) {
                    LOGGER.info("[transOtherPayDirectAlipayToPingan]他人代付，支付宝渠道，使用平安的navigate_path_toMini_alipay:prepayJumpCashierVo={}", JSON.toJSONString(prepayJumpCashierVo));
                    path = ApolloUtil.getProperty(Constants.NAVIGATE_PATH + "_" + CashierEnum.TO_MINI.getMode() + "_" + channel + "_" + Constants.PINGAN);
                }
            }
            // 如果是微信需要判断小是否需要替换成新的小程序
            if(Constants.CHANNEL_WX_NAME.equals(channel)) {
                JSONObject jsonPath = JSONObject.parseObject(path);
                if(thirdUserInfo != null && Constants.PINGAN.equals(thirdUserInfo.getThirdType())) {
                    jsonPath.put("WXMiniUserName", ApolloUtil.getProperty("pingan_mini_wx_username"));
                }
                // POP大商户切平安
                else if(Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), prepayJumpCashierVo.getAccountId())) {
                    if(Boolean.parseBoolean(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_TO_PINGAN_SWITCH, Boolean.FALSE.toString()))) {
                        jsonPath.put("WXMiniUserName", ApolloUtil.getProperty("pingan_mini_wx_username"));
                    }
                    else {
                        jsonPath.put("WXMiniUserName", ApolloUtil.getProperty("fbank_mini_wx_username"));
                    }
                }
                else if(bankConfigManager.isFuminBankAggregatePay(prepayJumpCashierVo.getBusinessOrderType())) {
                    jsonPath.put("WXMiniUserName", ApolloUtil.getProperty("fbank_mini_wx_username"));
                }
                path = jsonPath.toJSONString();
            }
            return String.format(path, prepayJumpCashierVo.getAmount(), payNoToken, prepayJumpCashierVo.getBusinessOrderNo(), prepayJumpCashierVo.getSubject());
        }
    }
    private String getH5NavigatePath(String path, PrepayJumpCashierVo prepayJumpCashierVo, String payNo) throws PaymentException {
        String payNoToken;
        try {
            payNoToken =  URLEncoder.encode(getPayNoSecret(payNo), "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }
        return path + "?amount=" + prepayJumpCashierVo.getAmount() + "&payNo=" + payNoToken+ "&subject=" + prepayJumpCashierVo.getSubject()+ "&businessNo=" + prepayJumpCashierVo.getBusinessOrderNo();
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }
}
